package cn.anecansaitin.whimsy.common.extension.comboSystem;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
//todo 添加可失败技能，判定技能失败的谓词，技能失败时的行为
public class SkillGraph {
    private final ISkill[] skills;
    private final Edge[][] edges;

    private SkillGraph(ISkill[] skills, Edge[][] edges) {
        this.skills = skills;
        this.edges = edges;
    }

    public Result getDeriveIndex(ISkill previous, int key) {
        int index = -1;

        for (int i = 0; i < skills.length; i++) {
            if (skills[i].equals(previous)) {
                index = i;
            }
        }

        if (index == -1) {
            System.out.println("技能派生发生了错误，无法在技能树中找到前置技能！！");
            return Result.notFound();
        }

        return getDeriveIndex(index, key);
    }

    public Result getDeriveIndex(int previous, int key) {
        Edge[] candidateEdge = edges[previous];

        for (Edge edge : candidateEdge) {
            if (edge.checkKey(key)) {
                return Result.found(getSkill(edge.target), edge.target);
            }
        }

        return Result.notFound();
    }

    public Result getEmpty() {
        return Result.found(ISkill.EMPTY, 0);
    }

    public ISkill getSkill(int index) {
        return skills[index];
    }

    public static Builder build() {
        return new Builder();
    }

    public record Result(ResultState state, ISkill skill, int index) {
        public static Result found(ISkill skill, int index) {
            return new Result(ResultState.FOUND, skill, index);
        }

        public static Result notFound() {
            return new Result(ResultState.NOT_FOUND, ISkill.EMPTY, -1);
        }

        public enum ResultState {
            FOUND,
            NOT_FOUND
        }
    }

    private record Edge(int target, int key) {
        public boolean checkKey(int key) {
            return this.key == key;
        }
    }

    public static class Builder {
        private final HashSet<ISkill> skills = new HashSet<>();
        private final HashMap<ISkill, HashMap<Integer, ISkill>> edges = new HashMap<>();
        private boolean noPreFlag = true;


        private Builder() {}

        public Builder noPreDerive(ISkill derive, int key) {
            if (derive == null) {
                throw new IllegalArgumentException("不能派生为null。");
            }

            skills.add(derive);
            addEdge(ISkill.EMPTY, derive, key);
            noPreFlag = false;
            return this;
        }

        public Builder derive(ISkill previous, ISkill derive, int key) {
            if (previous == null || ISkill.EMPTY == previous) {
                throw new IllegalArgumentException("不能由null或空技能作为派生前置。");
            }

            skills.add(previous);

            if (derive == null) {
                throw new IllegalArgumentException("不能派生为null。");
            }

            skills.add(derive);
            addEdge(previous, derive, key);
            return this;
        }

        public SkillGraph create() {
            if (noPreFlag) {
                throw new IllegalStateException("没有设置无前置的技能，意味着没有办法执行任何技能！！");
            }

            ISkill[] array = skills.toArray(new ISkill[0]);
            ISkill[] skillsArray = new ISkill[array.length + 1];
            skillsArray[0] = ISkill.EMPTY;
            //确保索引0为EMPTY，作为无需前置技能的前置
            for (int i = 0, arrayLength = array.length; i < arrayLength; i++) {
                skillsArray[i + 1] = array[i];
            }

            Edge[][] edgesArray = new Edge[skillsArray.length][];

            for (int i = 0, skillsArrayLength = skillsArray.length; i < skillsArrayLength; i++) {
                ISkill skill = skillsArray[i];
                HashMap<Integer, ISkill> deriveSkills = edges.getOrDefault(skill, new HashMap<>());
                Edge[] deriveEdges = new Edge[deriveSkills.size()];
                edgesArray[i] = deriveEdges;
                int j = 0;

                for (Map.Entry<Integer, ISkill> entry : deriveSkills.entrySet()) {
                    ISkill value = entry.getValue();

                    for (int k = 0, arrayLength = skillsArray.length; k < arrayLength; k++) {
                        ISkill prSkill = skillsArray[k];

                        if (prSkill == value) {
                            deriveEdges[j] = new Edge(k, entry.getKey());
                            j++;
                            break;
                        }
                    }
                }
            }

            return new SkillGraph(skillsArray, edgesArray);
        }

        private void addEdge(ISkill previous, ISkill derive, int key) {
            if (edges.containsKey(previous)) {
                edges.get(previous).put(key, derive);
            } else {
                HashMap<Integer, ISkill> edgeSet = new HashMap<>();
                edges.put(previous, edgeSet);
                edgeSet.put(key, derive);
            }
        }
    }
}
